/**
 * 手写实现MyPromise源码
 * 要求：尽可能还原Promise中的每一个API，通过注释的方式描述思路和原理
 */

/**
 * 1. promise是一个类，在执行这个类的时候，需要传递一个执行器进去，执行器会立即执行
 * 2. promise有三个状态，分别为等待pending、成功resolve、失败rejected
 *   状态一旦确定就不能被改变
 *   pending->resolve
 *   pending->rejected
 * 3. resolve和reject函数是用来更改状态的
 *   resolve:fufilled
 *   reject:rejected
 * 4. then方法做的事情就是判断状态，如果状态是成功，调用成功回调函数，如果是失败，调用失败函数，then方法是被定义在原型对象中
 * 5. then成功回调有一个参数，表示成功之后的值，失败回调有一个参数，表示失败的原因
 * 6. 同一个promise对象下的then方法可以被调用多次
 * 7. then方法可以被链式调用，后面then方法的回调函数拿到的值是上一个then方法的回调函数的返回值
 */

const PEDDING = 'pedding' //等待
const FUFILLED = 'fufilled' //成功
const REJECT = 'reject' //失败

class MyPromise {
  constructor(exeuctor) {
    try {
      exeuctor(this.resolve, this.reject)
    } catch (error) {
      this.reject(error)
    }
  }

  // promise状态
  status = PEDDING
  // 成功之后的值
  value = undefined
  // 失败之后的原因
  reason = undefined
  // 成功回调
  // successCallback = undefined  只能处理一个回调函数
  successCallback = []
  // 失败回调
  // failCallback = undefined
  failCallback = []


  // 使用箭头函数定义是为了执行方法的时候让this指向MyPromise的实例对象
  resolve = value => {
    // 如果状态不是等待，向下执行
    if (this.status !== PEDDING) return
    this.status = FUFILLED
    // 保存成功之后的值
    this.value = value
    // 判断成功回调是否存在，如果存在则调用
    // this.successCallback && this.successCallback(this.value)
    while (this.successCallback.length) {
      // this.successCallback.shift()(this.value)
      this.successCallback.shift()()
    }
  }

  reject = reason => {
    if (this.status !== PEDDING) return
    // 更改状态为失败
    this.status = REJECT
    // 保存失败后的原因
    this.reason = reason
    // 判断失败回调是否存在 若存在 调用
    // this.failCallback && this.failCallback(this.reason)
    while (this.failCallback.length) {
      // this.failCallback.shift()(this.reason)
      this.failCallback.shift()()
    }
  }

  then(successCallback, failCallback) {
    // 参数可选
    successCallback = successCallback ? successCallback : value => value
    // 参数可选
    failCallback = failCallback ? failCallback : reason => {
      throw reason
    }
    let promise2 = new MyPromise((resolve, reject) => {
      if (this.status === FUFILLED) {
        setTimeout(() => {
          try {
            let x = successCallback(this.value);
            // 判断 x 的值是普通值还是promise对象
            // 如果是普通值 直接调用resolve 
            // 如果是promise对象 查看promsie对象返回的结果 
            // 再根据promise对象返回的结果 决定调用resolve 还是调用reject
            resolvePromise(promise2, x, resolve, reject)
          } catch (e) {
            reject(e);
          }
        }, 0)
      } else if (this.status === REJECT) {
        setTimeout(() => {
          try {
            let x = failCallback(this.reason);
            // 判断 x 的值是普通值还是promise对象
            // 如果是普通值 直接调用resolve 
            // 如果是promise对象 查看promsie对象返回的结果 
            // 再根据promise对象返回的结果 决定调用resolve 还是调用reject
            resolvePromise(promsie2, x, resolve, reject)
          } catch (e) {
            reject(e);
          }
        }, 0)
      } else {
        // 状态为pedding，等待
        // 将成功回调和失败回调存储起来
        // this.successCallback.push(successCallback)
        this.successCallback.push(() => {
          setTimeout(() => {
            try {
              let x = successCallback(this.value);
              // 判断 x 的值是普通值还是promise对象
              // 如果是普通值 直接调用resolve 
              // 如果是promise对象 查看promsie对象返回的结果 
              // 再根据promise对象返回的结果 决定调用resolve 还是调用reject
              resolvePromise(promsie2, x, resolve, reject)
            } catch (e) {
              reject(e);
            }
          }, 0)
        })
        // this.failCallback.push(failCallback)
        this.failCallback.push(() => {
          setTimeout(() => {
            try {
              let x = failCallback(this.reason);
              // 判断 x 的值是普通值还是promise对象
              // 如果是普通值 直接调用resolve 
              // 如果是promise对象 查看promsie对象返回的结果 
              // 再根据promise对象返回的结果 决定调用resolve 还是调用reject
              resolvePromise(promsie2, x, resolve, reject)
            } catch (e) {
              reject(e);
            }
          }, 0)
        })
      }
    })
    return promise2
  }

  finally(callback) {
    return this.then(value => {
      return MyPromise.resolve(callback()).then(() => value)
    }, reason => {
      return MyPromise.resolve(callback()).then(() => {
        throw reason
      })
    })
  } catch (failCallback) {
    return this.then(undefined, failCallback)
  }


  static all(array) {
    let result = []

    return new MyPromise((resolve, reject) => {
      let count = 0

      function addData(index, value) {
        result[index] = value
        count++
        console.log(count, array.length)
        if (count === array.length) {
          resolve(result)
        }
      }
      for (let i = 0; i < array.length; i++) {
        let current = array[i]
        if (current instanceof MyPromise) {
          // Promise对象
          current.then((value) => {
            addData(i, value)
          }, (reason) => {
            reject(reason)
          })
        } else { //普通值
          addData(i, current)
        }
      }
    })
  }


  static resolve(value) {
    if (value instanceof MyPromise) {
      return value
    }
    return new MyPromise(resolve => resolve(value))
  }

}

function resolvePromise(promise, x, resolve, reject) {
  if (promise === x) {
    return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
  }
  if (x instanceof MyPromise) {
    x.then(resolve, reject)
  } else {
    resolve(x)
  }
}

module.exports = MyPromise